/* Copyright (C) 2021 Rain */

/* This file is part of CFS. */

/* 
  CFS is free software: you can redistribute it and/or modify 
  it under the terms of the GNU General Public License as published by 
  the Free Software Foundation, either version 3 of the License, or 
  (at your option) and later version. 
*/

/*
  CFS is distributed in the hope that it will be useful, 
  but WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  GNU General Public License for more details. 
*/

/*
  You should have received a copy of the GNU General Public License 
   along with CFS. If not, see <https://www.gnu.org/licenses/>.  
*/




#ifndef __CFS_H
#define __CFS_H

#include <sys/types.h>



typedef __uint32_t __inode_t;

#define BLOCK_SIZE  4096
#define BLOCK_SHIFT 12ul
#define BLOCK_MASK  ~(BLOCK_SIZE - 1)
#define ALIGNUP_BLOCK(n) (((n) + BLOCK_SIZE - 1) & BLOCK_MASK)

/* a map that describes block - inodes */
/* should I call it ibm or bim? :-) */

struct bim_entry {
	__inode_t inode;
	__uint32_t th_block;

/* we don't need this, because this is already 4-bytes aligned */
} __attribute__ ((packed));


struct inode {

#define ROOT_INO 2

	__inode_t inode;
	__inode_t pinode;

#define A_ROOTRD  (0x01)
#define A_ROOTWR  (0x02 | A_ROOTRD)

#define A_ROOT_RW (A_ROOTWR | A_ROOTRD)

#define A_USERRD  (0x04 | A_ROOTRD)
#define A_USERWR  (0x08 | A_ROOT_RW)

#define A_USER_RW (A_USERWR | A_ROOT_RW)

#define A_REGFILE 0x10
#define A_DIR     0x20

/* A_DIR can exists with A_LINK */

#define A_LINK    0x40

/* directory must be executable */
#define A_EXEC    0x80

/* we support blkdev & chrdev etc in vfs, not here */

	__uint8_t access;

	/* these also in link files */

	/* aligned blocks nr */
	__uint32_t blocks_nr;

	/* blocks_nr * BLOCK_SIZE + upsize = fsize (maybe a __uint64_T) */
	__uint32_t upsize;

	/* create time */
	__uint32_t ctime;

	/* last write time */
	__uint32_t wtime;

	/* no access-time here */

	/* zero in link files */
	__uint8_t nlinks;

	/* creator's uid and gid */
	__uint16_t uid;
	__uint16_t gid;

	/* extend access (unuse now) */
	__uint16_t eaccess;
} __attribute__ ((packed));


#define FNAME_LEN 124

struct dentry {
	__inode_t ino;
	char name[FNAME_LEN];
} __attribute__ ((packed));

typedef __uint32_t __bn_t;
typedef __uint32_t __nr_t;

struct superblock {

#define MAGIC 0x01328e3b

	__uint32_t magic;

	__nr_t ninodes;
	__nr_t nblocks;

	/* blocks count of block-bitmap */
	__nr_t nb_bbm;
	__nr_t nb_ibm;

	/* block nr of bbm */
	__bn_t bbm_bn;
	__bn_t ibm_bn;

	__nr_t nb_bim;
	__nr_t nb_inodes;

	__bn_t bim_bn;
	__bn_t inodes_bn;

	/* 32 bytes */
	struct inode root_ino;
	
	__bn_t first_data_block;

	__inode_t next_inode;

	/* 12 bytes */
	__uint32_t zero[3];
#ifdef __CFS
} sb;
#else 
};
extern struct superblock sb;
#endif


struct bim_buffer {
	/* the block in device of this block (point to bim) */
	__bn_t this_block;
	__uint8_t dirty;
	
	/* real block the buffer[0] points to (a data block) */
	__nr_t bn_start;

	/* 4096 bytes, 512 entries  */
	struct bim_entry *buffer;
#ifdef __CFS
} bimbuf;
#else
};
extern struct bim_buffer bimbuf;
#endif

#ifndef __CFS

/* for applications */
int fs_fd;

#else 
extern int fs_fd;

#endif


int cfs_init(int);

void fill_super(int);
void init_bim_buffer(int);

__bn_t bmap(int, __inode_t, __bn_t);
__bn_t create_block(int, __inode_t, __bn_t);

void lsdir(int, struct inode *);
struct inode *search_file(int, struct inode *, char *);

struct inode *create_file(int, struct inode *, char *, __uint8_t);
struct inode *mkdir(int, struct inode *, char *, __uint8_t);

__uint32_t write_file(int, struct inode *, char *, __uint32_t);
__uint32_t read_file(int, struct inode *, char *, __uint32_t);

#ifndef __CFS

#define sync_ino(buf) 				write_inode(fs_fd, (buf)->inode, buf);

/* get real block of inode's bn-th block, returns real-block nr */
#define cfs_bmap(ino, bn)			bmap(fs_fd, inode, bn)

/* list the directory 'dir' */
#define cfs_lsdir(dir) 				lsdir(fs_fd, dir)

/* create a new block for inode 'inode' (of the bn-th block), 
 *and returns the block number 
 */
#define cfs_create_block(inode, bn)		create_block(fs_fd, inode, bn)

/* create a new directory (with a '.' and '..') with name 'name', attribute 'attr', 
 * returns the new one's inode 
 */
#define cfs_mkdir(dir, name, attr)		lsdir(fs_fd, dir, name, attr)

/* search the file 'name' in directory 'dir' */
#define cfs_search(dir, name)			search_file(fs_fd, dir, name)

/* create a new file in directory 'dir', with name 'name', attribute 'attr' */
#define cfs_create(dir, name, attr)		create_file(fs_fd, name, attr)

/* write/read a file 'inode' for 'nbytes' bytes, returns bytes write/read */
#define cfs_write(inode, buf, nbytes) 		write_file(fs_fd, inode, buf, nbytes)
#define cfs_read(inode, buf, nbytes)		read_file(fs_fd, inode, buf, nbytes)

#endif

#define fsize(inode)				((inode->blocks_nr << BLOCK_SHIFT) + inode->upsize)

#define rootdir (&sb.root_ino)

#endif

